/**
*	地图类，作flex的控件用 
*/
package com.imax.emap
{
	import com.imax.emap.core.Bounds;
	import com.imax.emap.core.LatLon;
	import com.imax.emap.core.LatLonBounds;
	import com.imax.emap.event.EMapEvent;
	import com.imax.emap.mapProvider.GoogleDituProvider;
	import com.imax.emap.mapProvider.IMapProvider;
	import com.imax.emap.overlay.Marker;
	import com.imax.emap.overlay.MarkerLayerOverlay;
	import com.imax.emap.overlay.TileLayerOverlay;
	import com.imax.emap.projection.IProjection;
	
	import flash.display.Graphics;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import mx.core.UIComponent;
	import mx.effects.Move;
	import mx.events.ResizeEvent;
	/**
	 * 鼠标滚轮动时候触发
	*/	
	[Event(name="mouseWheel",type="flash.events.MouseEvent")]
	/**
	 * 点击地图时触发
	*/	
	[Event(name="click",type="com.imax.emap.event.EMapEvent")]
	/**
	 * 地图大小改变时候触发
	*/	
	[Event(name="resize",type="mx.events.ResizeEvent")]
	/**
	 * 地图双击时候触发
	*/	
	[Event(name="doubleClick",type="flash.events.MouseEvent")]
	/**
	 * 地图中心点改变时候触发，缩放时候也会出发
	*/	
	[Event(name="centerChanged",type="com.imax.emap.event.EMapEvent")]
	
	public class EMap extends EventDispatcher implements IMap 
	{
		/**
		 *  所有层的容器
		 */		
		private var container:UIComponent;
		
		/**
		 *  用来存放地图瓦片层和标注层
		 */		
		private var layerHolder:UIComponent;
		
		/**
		 *  用来遮盖不应显示的地方
		 */		
		private var mask:UIComponent;
		
		/**
		 *  地图图片层，所有的地图图片都会放在这里 
		 */		
		private var tileLayerOverlay:TileLayerOverlay;
		
		/**
		 *  <p>标注层，存放标注，在地图图片层之上</p>  
		 */		
		private var markerLayerOverlay:MarkerLayerOverlay;
		
		/**
		 * 地图的providers 
		 */		
		private var mapTypes:Array;
		
		/**
		 * 当前地图种类
		 */		
		private var currentMapType:String;
		/**
		 * 地图缩放等级 
		 */		
		private var zoomLevel:Number;
		
		/**
		 * 上次保存的地图缩放等级 
		 */		
		private var previousZoomLevel:Number;
		/**
		 * 地图中心的经纬度 
		 */		
		private var centerLatLon:LatLon;
		/**
		 * 上次保存的地图中心的经纬度 
		 */	
		private var previousCenterLatLon:LatLon;
		
		
		
		/**
		 * 可视范围的中心点
		 */  
		private var _centerPoint:Point;
		
		/**
		 * 可视范围
		 */ 
		private var _viewBound:Bounds;
		
		
		/**
		 * 为了计算offset x y，拖拽时记录_layerHolder的x、y
		 */		
		private var _lastX:Number;
		private var _lastY:Number;
		
		/**
		 * 判断是否拖拽 
		 */		
		private var _mouseDown:Boolean;
		
		/**
		 * 地图的提供商类 
		 */		
		private var mapProvider:IMapProvider;
		
		/**
		 * 地图当前使用的投影，由mapProvider.getProjection()取得,这个属性要跟IMapProvider一起修改
		 */		
		private var projection:IProjection;
		
		/**
		 * 地图提供商的最大缩放比例
		 */		
		private var maxZoom:int 
		
		/**
		 * 地图提供商的最小缩放比例
		 */	
		private var minZoom:int;
		
		/**
		 * 地图的偏移度，由于可爱的政府要求地图要有非线性偏移，所以在这里加这个属性给大家用 
		 */		
		public var diffLatLon:LatLon = new LatLon(0,0);
		
		/**
		 * 
		 * @param _container 地图所用的flex控件
		 * @param _mapProvider 地图类型
		 * 
		 */		
		public function EMap(_container:UIComponent,_mapProvider:IMapProvider = null)
		{				
			this.container = _container;
				
			container.doubleClickEnabled = true;
			container.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown);
			container.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp);
			container.addEventListener(MouseEvent.ROLL_OUT, this.onMouseUp);	
			container.addEventListener(ResizeEvent.RESIZE, this.onMapResized);
			container.addEventListener(MouseEvent.DOUBLE_CLICK, this.onDbClick);
			container.addEventListener(MouseEvent.CLICK, this.onMapClick);
			container.addEventListener(MouseEvent.MOUSE_WHEEL, this.onMouseWheel);
			container.addEventListener(KeyboardEvent.KEY_DOWN , this.onKeyDown);
						
			layerHolder = new UIComponent();
			if(_mapProvider == null){
				this.mapProvider = new GoogleDituProvider();
			}
			else{
				this.mapProvider = _mapProvider;
			}
			tileLayerOverlay = new TileLayerOverlay(mapProvider);
			projection = mapProvider.getProjection();
			layerHolder.addChild(tileLayerOverlay);
					
			//标注层
			markerLayerOverlay = new MarkerLayerOverlay(projection);
			layerHolder.addChild(markerLayerOverlay);
			
			container.addChild(layerHolder);
			
			//地图瓦片层tileLayerOverlay的遮盖层，没有它地图瓦片层可是什么都显示哦
			mask = new UIComponent();
		    changeMask();
            mask.name ="_masker";
            container.addChild(mask);
            
            layerHolder.mask = mask;
            
			_viewBound = new Bounds(0, 0, container.width, container.height);
			_centerPoint = new Point(0,0);
			maxZoom = mapProvider.getMaxZoom();
			minZoom = mapProvider.getMinZoom();
		}
				
		private function onMouseWheel(evt:MouseEvent):void{
			//经调试，可能因为下载软件取得focus的原因，所以管轮动时候必须手工把mouseDown设成false才行,否则很有问题，一时ok一时不行
			this._mouseDown = false;
			if( (evt.delta) > 0 && (this.zoomLevel < this.maxZoom) ){
				this.zoom =this.zoomLevel+1;	
			} 
			if( (evt.delta) < 0 && (this.zoomLevel > this.minZoom) ){
			 	this.zoom =this.zoomLevel-1;
			}
			dispatchEvent(evt);
		}
		
		private function onMapClick(evt:MouseEvent):void{
			/* var p:Point = map2Global(new Point(evt.stageX, evt.stageY));*/	
			var clickedPoint:Point=new Point();
			clickedPoint.x = this.layerHolder.mouseX;
			clickedPoint.y = this.layerHolder.mouseY;
			var clickedEvent:EMapEvent = new EMapEvent(EMapEvent.CLICK);
			clickedEvent.data.latLon = this.projection.fromPixelToLatLon(clickedPoint,this.zoom);
			dispatchEvent(clickedEvent);
		}
		
		/**
		 *  发生resize
		 * @param evt
		 * 
		 */		
		private function onMapResized(evt:ResizeEvent):void{
			//this._centerPoint = new Point(width/2 - coverLayer.x, height/2 - coverLayer.y);
		 	this.centerPointPixel = this._centerPoint;
		 	changeMask();
		 	this._viewBound.width = container.width;
			this._viewBound.height = container.height;
			dispatchEvent(evt);
		}
		
		/**
		 * 双击
		 * @param evt
		 * 
		 */		
		private function onDbClick(evt:MouseEvent):void{
			moveMapBy(container.width/2-evt.stageX, container.height/2 - evt.stageY);
			this._mouseDown = false;
			dispatchEvent(evt);
		}
	
		/**
		 * 
		 * @param evt
		 * 
		 */		
		private function onMouseDown(evt:MouseEvent):void{
			//MapCursor.changeCursor(MapCursor.mapMoveCursor, -6, -6);
			
			_lastX = layerHolder.x;
			_lastY = layerHolder.y;
			
			layerHolder.startDrag();
			_mouseDown = true;
			//dispatchEvent(evt);
		}
		
		/**
		 * 鼠标释放
		 * @param evt
		 * 
		 */		
		private function onMouseUp(evt:MouseEvent):void{
			if(!this._mouseDown) return;
			//MapCursor.changeCursor(null, 0, 0);
			
			layerHolder.stopDrag();	

			var offsetX:Number = layerHolder.x - _lastX;
			var offsetY:Number = layerHolder.y - _lastY;

			
			//如果有了移动
			if((offsetX != 0) || (offsetY != 0)){
				this._centerPoint.offset(-offsetX,-offsetY);
				this.centerPointPixel = this._centerPoint;
				_mouseDown = false;
			}
			
			//不取得focus的话无法相应键盘事件
			container.setFocus();
		}
		
		private function onKeyDown(evt:KeyboardEvent):void{
			//经调试，可能因为下载软件取得focus的原因，所以管轮动时候必须手工把mouseDown设成false才行,否则很有问题，一时ok一时不行
			var key:int = evt.keyCode;
			//下面是按上下左右时的处理方法
			if(key == 38) moveMapBy(0,-1);
			if(key == 40) moveMapBy(0,1);
			if(key == 37) moveMapBy(-1,0);
			if(key == 39) moveMapBy(1,0);
			//dispatchEvent(evt);
		}
		
		/**
		 * 移动地图
		 * @param _x
		 * @param _y
		 * @param ani 是否平滑移动（动画效果）
		 * 
		 */		
		public function moveMapBy(_x:Number, _y:Number, ani:Boolean = true):void{
			var p:Point = new Point(this.centerPointPixel.x-_x, this.centerPointPixel.y-_y);
			
			if(ani){
				var mv:Move = new Move(this.layerHolder);
				mv.xTo = container.width/2-p.x;
				mv.yTo = container.height/2-p.y;
				mv.play();
			}			
			this.centerPointPixel = p;
		}
		
		/**
		 * 当container改变大小时候，也要相应改变mask 
		 * 
		 */		
		private function changeMask():void{
			var g:Graphics = mask.graphics;
			g.clear();
            g.beginFill(0x000000);
            g.drawRect(0,0,container.width,container.height);
            g.endFill();
		}
		
		/**
		 * 缩放 重定位地图中心 拖拉地图 均会激发这个方法
		 * 
		 */		
		private function viewBoundChanged():void{
			//应该改变所有的层, 这里直接使用了.
			//trace(_viewBound.toString());
			tileLayerOverlay.viewBoundChanged(this._viewBound, this.zoomLevel);
			markerLayerOverlay.viewBoundChanged(this._viewBound, this.zoomLevel);
			dispatchEvent(new Event("mapCenterChanged"));
		}
		
		
		
		//公开给用户的方法
		
		/**
		 * 返回地图上指定缩放比例下，某像素所在的经纬度 
		 * @param p 像素坐标
		 * @param _zoom 缩放等级
		 * @return 该像素对应的经纬度坐标
		 * 
		 */				
		public function fromPixelToLatLon(p:Point,_zoom:Number = -1):LatLon{
			if(_zoom == -1) return this.projection.fromPixelToLatLon(p,this.zoom);
			return this.projection.fromPixelToLatLon(p,_zoom);
		}
		
		/**
		 * 返回地图上指定缩放比例下，某经纬度所在的 像素
		 * @param latLon 经纬度
		 * @param _zoom 缩放比例
		 * @return 该经纬度对应的像素坐标
		 * 
		 */		
		public function fromLatLonToPixel(latLon:LatLon,_zoom:Number = -1):Point
		{
			if(_zoom == -1) return this.projection.fromLatLonToPixel(latLon,this.zoom);
			return this.projection.fromLatLonToPixel(latLon,_zoom);
		}
		
		/**
		 * 返回已经注册的地图类型（还没做，好像没必要） 
		 * @return 
		 * 
		 */		
		public function getMapTypes():Array
		{
			//TODO: implement function
			return null;
		}
		
		/**
		 * 
		 * @return 返回当前地图的名称（还没做，好像没必要）
		 * 
		 */		
		public function getCurrentMapType():String
		{
			return this.mapProvider.toString();
		}
		
		/**
		 * 注册地图类型（还没做，好像没必要） 
		 * @param type
		 * 
		 */		
		public function addMapType(type:String):void
		{
			//TODO: implement function
		}
		
		/**
		 * 删除地图类型（还没做，好像没必要） 
		 * @param type
		 * 
		 */		
		public function removeMapType(type:String):void
		{
			//TODO: implement function
		}
		
		/**
		 * 地图可视范围内的像素坐标区域 
		 * @return 像素坐标区域
		 * 
		 */		
		public function getPixelBounds():Bounds
		{
			//TODO: implement function
			return this._viewBound;
		}
		
		/**
		 * 地图可视范围内的经纬度坐标区域
		 * @return 经纬度坐标区域
		 * 
		 */		
		public function getLatLonBounds():LatLonBounds
		{
			var tempPoint:Point = new Point(0,0);
			tempPoint = this._viewBound.bottomRight;
			tempPoint.x -= this._viewBound.width;
			var sw:LatLon = this.projection.fromPixelToLatLon(tempPoint,this.zoom);
			tempPoint = this._viewBound.topLeft;
			tempPoint.x += this._viewBound.width;
			var ne:LatLon = this.projection.fromPixelToLatLon(tempPoint,this.zoom);
			return new LatLonBounds(sw,ne);
		}
		
		/**
		 * 
		 * @return 返回中心点的经纬度
		 * 
		 */		
		public function getCenter():LatLon{
			return projection.fromPixelToLatLon(this.centerPointPixel,this.zoom);
		}
		
		/**
		 * 定位地图中心点，参考google map api的
		 * @param centerLatLon 中心点的经纬度
		 * @param zoom 缩放等级
		 * 
		 */		
		public function setCenter(centerLatLon:LatLon, _zoom:int):void
		{
			this.zoomLevel = _zoom;
			centerLatLon.lat += this.diffLatLon.lat;
			centerLatLon.lon += this.diffLatLon.lon;
			//通过projection将经纬转化成 像素坐标,将像素坐标设定成_centerPointPixel,centerPointPixel然后调用viewBoundChanged()
			this.centerPointPixel = this.projection.fromLatLonToPixel(centerLatLon , _zoom);
			
		}
		/**
		 * 取得地图中心点，以像素为单位,该方法内部使用，对外只是getCenter():LatLon;
		 * 
		 * @return 地图中心点的像素
		 * 
		 */			
		private function get centerPointPixel():Point{
			return this._centerPoint;
		}
		/**
		 * 设定中心点的像素坐标，辅助setCenter(centerLatLon:LatLon, zoom:int)用
		 * @param p 地图中心点，以像素为单位
		 * 
		 */		
		private function set centerPointPixel(p:Point):void{
			this._centerPoint = p;
			this.layerHolder.x = (container.width/2-p.x);
			this.layerHolder.y = (container.height/2-p.y);
			
			this._viewBound.x = p.x - container.width/2;
			this._viewBound.y = p.y - container.height/2;
		
			viewBoundChanged();
			//trace("地图中心点像素坐标"+p.toString());	
			//trace("标注层中心点坐标:"+this.projection.fromLatLonToPixel(this.getCenter(),this.zoom).toString()  );
			//trace("瓦片层坐标："+this.tileLayerOverlay.x+"/"+this.tileLayerOverlay.y);
			//trace("标注层坐标："+this.markerLayerOverlay.x+"/"+this.markerLayerOverlay.y);
		}
		
		/**
		 * 设定地图的缩放比例 
		 * @param _zoom 缩放比例（每个地图都不同,基本在1--19的范围内，灵图是3--17）
		 * 
		 */		
		public function set zoom(_zoom:Number):void{
			//保证_zoom在地图提供的范围内
			if(_zoom < minZoom) _zoom = minZoom;
			if(_zoom > maxZoom) _zoom = maxZoom;
			
			if(this.zoomLevel == _zoom) return;

			var z:Number = Math.pow(2, (_zoom - this.zoomLevel ));
			this.zoomLevel = _zoom;
			//trace("z:"+z);
			var p:Point = new Point(this._centerPoint.x * z, this._centerPoint.y * z);
			this.centerPointPixel = p;
		}
		
		/**
		 * 
		 * @return 返回当前的缩放比例
		 * 
		 */		
		public function get zoom():Number{
			return this.zoomLevel;
		}
		
		
		public function savePosition():void
		{
			this.previousZoomLevel = this.zoom;
			this.previousCenterLatLon = this.getCenter();
		}
		
		public function returnToSavedPosition():void
		{
			this.setCenter(this.previousCenterLatLon,this.previousZoomLevel);
		}
		
		/**
		 * 往地图上添加标注
		 * @param marker 标注
		 * 
		 */		
		public function addMarker(marker:Marker):void
		{
			markerLayerOverlay.addMarker(marker);
		}
		
		/**
		 * 删除地图上某个标注 
		 * @param marker 标注
		 * 
		 */		
		public function removeMarker(marker:Marker):void
		{
			markerLayerOverlay.removeMarker(marker);
		}
		
		/**
		 * 清除地图上的所有标注 
		 * 
		 */		
		public function clearMarkers():void
		{
			markerLayerOverlay.clearMarkers();
		}
		
		/**
		 *  
		 * @return 返回所有标注 
		 * 
		 */		
		public function getMarkers():Array
		{
			return markerLayerOverlay.getMarkers();
		}
		
		/**
		 * 设定地图的大小，以像素为单位
		 * @param mapWidth 宽度
		 * @param mapHeight 高度
		 * 
		 */		
		public function setSize(mapWidth:Number,mapHeight:Number):void{
			if(mapWidth == this.container.width && mapHeight == this.container.height) return;
			//hangeMask();
			//this.container.width = mapWidth;
			//this.container.height = mapHeight;
			this.container.setActualSize(mapWidth,mapHeight);
			this._viewBound.width = mapWidth;
			this._viewBound.height = mapHeight;
		}
		
	}
}